package com.hmall.search.service.impl;

import cn.hutool.core.util.ObjectUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.FunctionBoostMode;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.json.JsonData;
import com.hmall.common.domain.PageDTO;
import com.hmall.search.domain.po.ItemDoc;
import com.hmall.search.domain.query.ItemPageQuery;
import com.hmall.search.service.ISearchService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.client.Response;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;


@Service
@Slf4j
@AllArgsConstructor
public class SearchServiceImpl implements ISearchService {

    private final ElasticsearchClient esClient;


    @Override
    public PageDTO<ItemDoc> esSearch(ItemPageQuery query) throws IOException {
        SearchResponse<ItemDoc> response = esClient.search(builder->builder
                .index("items")
                .query(queryBuilder->queryBuilder
                        .functionScore(fs->fs
                                .query(qBuilder->qBuilder
                                        .bool(b->{
                                            if(ObjectUtil.isNotEmpty(query.getKey())){
                                                b.must(m->m.match(mb->mb.field("name").query(query.getKey())));
                                            }
                                            if(ObjectUtil.isNotEmpty(query.getCategory())){
                                                b.filter(f->f.term(t->t.field("category").value(query.getCategory())));
                                            }
                                            if(ObjectUtil.isNotEmpty(query.getBrand())){
                                                b.filter(f->f.term(t->t.field("brand").value(query.getBrand())));
                                            }
                                            if(ObjectUtil.isNotEmpty(query.getMaxPrice()) && ObjectUtil.isNotEmpty(query.getMinPrice())){
                                                b.filter(f->f.range(r->r.field("price").lte(JsonData.of(query.getMaxPrice())).gte(JsonData.of(query.getMinPrice()))));
                                            }
                                            return b;
                                        }))
                                .functions(fBuilder->fBuilder
                                        .filter(f->f.term(t->t.field("isAD").value(true)))
                                        .weight(10D))
                                .boostMode(FunctionBoostMode.Multiply)))
                .sort(sBuilder->sBuilder.field(fBuilder->{
                    if(ObjectUtil.isEmpty(query.getSortBy())){
                        fBuilder.field("_score").order(SortOrder.Desc);
                    }else{
                        fBuilder.field(query.getSortBy()).order(query.getIsAsc()?SortOrder.Asc:SortOrder.Desc);
                    }
                    return fBuilder;
                }))
                .highlight(hlBuilder->hlBuilder
                        .fields("name", fBuilder->fBuilder
                                .preTags("<em style='color:red;'>")
                                .postTags("</em>")))
                .from((query.getPageNo()-1)* query.getPageSize()).size(query.getPageSize()), ItemDoc.class);
        List<ItemDoc> list = new ArrayList<>();
        long total = response.hits().total().value();
        List<Hit<ItemDoc>> hits = response.hits().hits();
        for (Hit<ItemDoc> hit : hits) {
            ItemDoc itemDoc = hit.source();
            Map<String, List<String>> highlight = hit.highlight();
            if(ObjectUtil.isNotEmpty(highlight)){
                List<String> strings = highlight.get("name");
                if(ObjectUtil.isNotEmpty(strings)){
                    itemDoc.setName(strings.get(0));
                }
            }
            list.add(itemDoc);
        }
        PageDTO<ItemDoc> dto = new PageDTO<>();
        dto.setTotal(total);
        dto.setList(list);
        return dto;
    }
}
